package org.mte.sak.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Properties;

/**
 * Legge e scrive un file di properties.
 * In input al costruttore è necessario passare il nome del file di <i>properties</i> che
 * si vuole caricare.
 * Se il nome del file è privo di percorso completo deve essere preceduto dal
 * carattere "/", in questo caso automaticamente verrà cercato il file in tutto
 * il <i>classpath</i>.
 *
 * @author Marco Traversari
 * @version $Revision: 1.2 $ $Date: 2011-04-20 08:34:19 $
 *
 * @see <code>java.util.Properties</code>
 * @deprecated
 */
public class PropertiesHandler {

	/**
	 * Definizione file chiave/valore
	 */
	public static final String DEF = "properties";

    /**
     * Estensione del file chiave/valore.
     */
    public static final String EXT = "." + DEF;

	/**
	 * Oggetto <code>Properties</code> principale in cui viene caricato
	 * il file di properties.
	 */
	protected Properties props = null;

	/**
	 * Costruttore vuoto.
	 */
	public PropertiesHandler() {
	}

    /**
     * Instanzia anche l'oggetto
     * <code>Properties</code> incapsulato.
     *
     * @param isEmpty
     */
    public PropertiesHandler(boolean isEmpty) {
        if (isEmpty) {
            props = new Properties();
        }
    }

	/**
	 * Costruttore con il percorso del file di properties
	 * da leggere.
	 *
	 * @param filePath
	 * @throws IOException
	 */
	public PropertiesHandler(String filePath) throws IOException {
		loadFile(filePath);
	}

    /**
	 * Legge un file di properties.
	 *
	 * @param filePath percorso assolut del file di properties.
	 * @throws IOException.
	 */
   	public void loadFile(String filePath) throws IOException {
		BufferedInputStream bufferedInputStream = null;
		if (props == null) {
			props = new Properties();
		}
		bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
		props.load(bufferedInputStream);
		bufferedInputStream.close();
	}

	/**
	 * Legge un file di properties presente nel classpath.
     *
	 * @param filePath percorso relativo del classpath del file di properties.
	 * @throws IOException.
	 */
	public void loadConfigFile(String filePath) throws IOException {
		if (props == null) {
			props = new Properties();
		}
		InputStream inputStream = getClass().getResourceAsStream(filePath);
		props.load(inputStream);
		inputStream.close();
	}

	/**
	 * <p>Carica una file di properties in funzione
	 * del <code>Locale</code> specificato.</p>
     *
     * <p>Se il percorso assoluto del file è <code>null</code>,
     * si aspetta che <code>baseName</code> indica una risorsa
     * nel classpath.</p>
     *
	 * @param baseName nome del file di properties senza estensione
	 * @param path percorso assoluto dei file
     *          deve finire con il carattere <i>/</i>
	 * @param locale indica la lingua da utilizzare
	 * @throws IOException
	 */
	public void loadLanguageFile(String baseName, String path,
	        	Locale locale) throws IOException {
	    String language = "";
	    if (locale != null) {
	        language = locale.getLanguage();
	    }
	    if (path != null) {
	        String filePath = null;
		    boolean isFound = false;
	        String[] filePaths = {
	                path + baseName + "_" + language + EXT,
	    			path + baseName + EXT};
	        int i = 0;
	        while (i < filePaths.length && !isFound) {
	            filePath = filePaths[i];
	            File file = new File(filePath);
	            isFound = file.exists();
		        i++;
	        }
            if (isFound) {
                loadFile(filePath);
            }
	    } else {
	        if (baseName.indexOf(".") != -1) {
	            baseName = "/" + baseName.replace('.', '/');
	        }
	        String[] filePaths = {
	                baseName + "_" + language + EXT,
	    			baseName + EXT};
	        InputStream inputStream = null;
	        int i = 0;
	        while (i < filePaths.length && inputStream == null) {
	            inputStream = getClass().getResourceAsStream(filePaths[i]);
	            i++;
	        }
	        if (inputStream != null) {
	        	if (props == null) {
	    			props = new Properties();
	    		}
	            props.load(inputStream);
				inputStream.close();
	        }
	    }
	}

	/**
	 * Consente di salvare l'oggetto <code>Properties</code> instanziato nel
	 * caso siano avvenute delle modifiche. L'operazione sarà registrata etichettando
	 * con la data corrente l'inizio del file salvato.
	 *
	 * @param filePath è il percorso del file da salvare.
	 *
	 * @throws IOException se si verificano errori I/O.
	 */
	public void store(String filePath) throws IOException {
		store(filePath, null);
	}

	/**
	 * Consente di salvare l'oggetto <code>Properties</code> istanziato nel
	 * caso siano avvenute delle modifiche. È possibile definire anche un'etichetta
	 * con cui registrare il cambiamento del file.
	 *
	 * @param filePath è il percorso del file da salvare.
	 * @param header è l'etichetta che verrà posta in capo al file.
	 *
	 * @throws IOException se si verificano errori I/O.
	 */
	public void store(String filePath, String header) throws IOException {
		OutputStream outputStream = new FileOutputStream(filePath);
		props.store(outputStream, header);
	}

	/**
	 * <p>Restituisce un valore del file di properties in formato
	 * <code>boolean</code>.</p>
	 * <p>Ci si aspetta che la chiave passata in input corrisponda ad
	 * valore tipo <i>yes,no</i> o <i>true,false</i>.</p>
	 * <p>Se la chiave non è trovata verrà restituito <code>false</code></p>.
	 *
	 * @param key la chiave per la quale serve il valore.
	 *
	 * @return <code>true</code> o <code>false</code> secondo il valore letto.
	 */
	public boolean getBoolean(String key) {
		return getBoolean(key, "");
	}

	/**
	 * <p>Restituisce un valore del file di properties in formato
	 * <code>boolean</code>.</p>
	 * <p>Ci si aspetta che la chiave passata in input corrisponda ad un
	 * valore tipo <i>yes,no</i> o <i>true,false</i>.</p>
	 * <p>Se la chiave non � trovata verr� restituito <code>booleanValue</code></p>.
	 *
	 * @param key la chiave per la quale serve il valore.
	 * @param booleanValue � il valore di default di tipo <code>boolean</code>.
	 *
	 * @return <code>true</code> o <code>false</code> secondo il valore letto.
	 */
	public boolean getBoolean(String key, boolean booleanValue) {
		String defaultValue = "" + booleanValue;
		return getBoolean(key, defaultValue);
	}

	/**
	 * <p>Restituisce un valore del file di properties in formato
	 * <code>boolean</code>.</p>
	 * <p>Ci si aspetta che la chiave passata in input corrisponda ad
	 * valore tipo <i>yes,no</i> o <i>true,false</i>.</p>
	 * <p>� possibile passare un valore di <i>default</i> nel caso
	 * che la chiave non � trovata.</p>
	 *
	 * @param key la chiave per la quale serve il valore.
	 * @param defaultValue valore di <i>default</i> se la chiave non � trovata.
	 *
	 * @return <code>true</code> o <code>false</code> secondo il valore letto.
	 */
	public boolean getBoolean(String key, String defaultValue) {
		String value = props.getProperty(key, defaultValue).trim();
		boolean isTrue = (value.equals("yes") || value.equals("true")) ? true : false;
		return isTrue;
	}

    /**
	 * <p>Restituisce un oggetto di tipo <code>Boolean</code> istanziato
     * con il valore del file di properties.</p>
	 * <p>Ci si aspetta che la chiave passata in input corrisponda ad un
	 * valore tipo <i>yes,no</i> o <i>true,false</i>.</p>
	 * <p>� possibile passare un valore di <i>default</i> nel caso
	 * che la chiave non � trovata.</p>
	 *
	 * @param key la chiave per la quale serve il valore.
	 *
	 * @return <code>true</code> o <code>false</code> secondo il valore letto.
     */
	public Boolean getBooleanObject(String key) {
		return new Boolean(getBoolean(key, ""));
	}

    /**
	 * <p>Restituisce un oggetto di tipo <code>Boolean</code> istanziato
     * con il valore del file di properties.</p>
	 * <p>Ci si aspetta che la chiave passata in input corrisponda ad un
	 * valore tipo <i>yes,no</i> o <i>true,false</i>.</p>
	 * <p>� possibile passare un valore di <i>default</i> nel caso
	 * che la chiave non � trovata.</p>
	 *
	 * @param key la chiave per la quale serve il valore.
	 * @param booleanValue valore di <i>default</i> se la chiave non � trovata.
	 *
	 * @return <code>true</code> o <code>false</code> secondo il valore letto.
     */
	public Boolean getBooleanObject(String key, boolean booleanValue) {
		return new Boolean(getBoolean(key, booleanValue));
	}

	/**
	 * Restituisce il valore della chiave definita nel file di <i>properties</i>.
	 *
	 * @param key
	 * @return il valore associato alla chiave specificata.
	 */
	public String getProperty(String key) {
		return props.getProperty(key);
	}

	/**
	 * Restituisce il valore della chiave definita nel file di <i>properties</i>.
	 * Se la chiave non � trovata restituisce <code>null</code>, in caso contrario
	 * al valore vengono eliminati gli spazi bianchi.
	 *
	 * @param key � la chiave di una valore del file di <i>properties</i>.
	 *
	 * @return il valore associato alla chiave specificata.
	 */
	public String getString(String key) {
		String value = props.getProperty(key);
		value = (value != null) ? value.trim() : value;
		return value;
	}

	/**
	 * Restituisce il valore della chiave definita nel file di <i>properties</i>.
	 *
	 * @param key � la chiave di una valore del file di <i>properties</i>.
	 * @param defaultValue � il valore restituito se la chiave non � trovata.
	 *
	 * @return il valore associato alla chiave specificata.
	 */
	public String getString(String key, String defaultValue) {
		String value = props.getProperty(key, defaultValue);
		value = (value != null) ? value.trim() : value;
		return value;
	}

	/**
	 * Utilizza <code>getInt(String key, int intDefaultValue)</code>.
	 * con <code>intDefaultValue = -1</code>.
	 *
	 * @param key � la chiave di una valore del file di <i>properties</i>.
	 *
	 * @return un tipo <code>int</code> che rappresenta il valore associato
	 * 				alla chiave specificata.
	 */
	public int getInt(String key) {
		return getInt(key, -1);
	}

	/**
	 * Restituisce il valore della chiave definita nel file di <i>properties</i>,
	 * trasformandolo in un <code>int</code> se il valore non � trovato ritorna il valore
	 * di default specificato.
	 *
	 * @param key � la chiave di una valore del file di <i>properties</i>.
	 * @param intDefaultValue � il valore di default ritornato nel caso che
	 * 			la chiave sia <code>null</code>.
	 *
	 * @return un tipo <code>int</code> che rappresenta il valore associato
	 * 				alla chiave specificata.
	 */
	public int getInt(String key, int intDefaultValue) {
		int intValue = getInteger(key, intDefaultValue).intValue();
		return intValue;
	}

	/**
	 * Utilizza <code>getInteger(String key, int intDefaultValue)</code>.
	 * con <code>intDefaultValue = -1</code>.
	 *
	 * @param key � la chiave di una valore del file di <i>properties</i>.
	 *
	 * @return un tipo <code>Integer</code> che rappresenta il valore associato
	 * 				alla chiave specificata.
	 */
	public Integer getInteger(String key) {
		return getInteger(key, -1);
	}

	/**
	 * Restituisce il valore della chiave definita nel file di <i>properties</i>,
	 * trasformandolo in un <code>Integer</code> se il valore non � trovato ritorna il valore
	 * di default specificato.
	 *
	 * @param key � la chiave di una valore del file di <i>properties</i>.
	 * @param intDefaultValue � il valore di default ritornato nel caso che
	 * 			la chiave sia <code>null</code>.
	 *
	 * @return un tipo <code>int</code> che rappresenta il valore associato
	 * 				alla chiave specificata.
	 */
	public Integer getInteger(String key, int intDefaultValue) {
		String value = props.getProperty(key);
		Integer integerValue = (value != null) ? Integer.valueOf(value.trim()) : new Integer(intDefaultValue);
		return integerValue;
	}


	/**
	 * <p>Riempie un ArrayList con valori presenti nell'oggetto
	 * properties caratterizzati da un chiave che ha una base fissa
	 * e un indice</p>
	 * <p>Es: baseKey=item</p>
	 * <ul>
	 * <li>item.0=value-0</li>
	 * <li>...</li>
	 * <li>item.n=value-n</li>
	 * </ul>
	 * @param baseKey
	 * @return values
	 */
	public ArrayList<String> getArrayList(String baseKey) {
		ArrayList<String> values = null;
		String value = null;
		int pos = 0;
		while ((value = (String) props.get(baseKey + "." + pos)) != null) {
			if (values == null) {
				values = new ArrayList<String>();
			}
			values.add(value);
			pos++;
		}
		return values;
	}

	/**
	 * Permette di impostare delle coppie chiave, valore nell'oggetto <code>Properties</code>
	 * definito.
	 *
	 * @param key è la chiave di una voce.
	 * @param value il valore di una voce.
	 */
	public void setProperty(String key, String value) {
		props.setProperty(key, value);
	}

	/**
	 * Restituisce l'oggetto <code>Properties</code> instanziato.
	 *
	 * @return un oggetto di tipo <code>Properties</code>.
	 */
	public Properties getProperties() {
		return props;
	}

    /**
     * Imposta un oggetto di tipo <code>Properties</code>.
     *
     * @param props oggetto da impostare.
     */
	public void setProperties(Properties props) {
		this.props = props;
	}

	/**
	 * merge
	 * @param filePath
	 * @throws IOException
	 */
	public void merge(String filePath) throws IOException  {
		PropertiesHandler ph = new PropertiesHandler();
		ph.loadFile(filePath);
		merge(ph.getProperties());
	}

	/**
	 * merge
	 * @param mergeProps
	 */
	public void merge(Properties mergeProps) {
		if (props != null && mergeProps != null) {
			Enumeration<Object> en = mergeProps.keys();
			while (en.hasMoreElements()) {
				String key = (String) en.nextElement();
				String value = mergeProps.getProperty(key);
				if (value != null && !value.equals("")) {
					props.setProperty(key, value);
				} else {
					props.remove(key);
				}
			}
		}
	}
}